package com.hxk.jvm;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Random;

/**
 * jvm的垃圾回收Gc
 *    GC的垃圾回收算法有4种：
 *      1、引用计算
 *      2、复制算法
 *      3、标记清除算法
 *      4、标记清除整理算法
 * jvm是如何确定垃圾？
 *      JVM是通过做GCRoots的可达性分析，来确定要回收的垃圾。
 *         GCRoots是什么？
 *              是一组四种对象的根集合体。4种对象分别为:
 *              1、虚拟机栈（栈桢中的局部变量区，也叫做局部变量表）
 *              2、方法区中的类静态属性引用的对象
 *              3、方法区中常量引用的对象
 *              4、本地方法栈中Native方法引用的对象
 *          从这四种对象作为垃圾回收扫描的起始点
 *  JVM通过GCRoots的可达性分析后，会对引用对象做4种归类：
 *       1、强引用，即使jvm爆了（OOM）也不能清除的引用对象
 *       2、软引用，当jvm要爆了时，会被清除的引用对象数据
 *       3、弱引用，当GC开始清除垃圾时就会被清除
 *       4、虚引用，必须配合队列一起使用，常用于处理一下在要清除该对象引用时，在干点其他的事情
 *   JVM的垃圾收集器有哪些：
 *      1、串行收集器，该收集器是为单线程环境下设计的，它是使用一个垃圾收集线程进行垃圾回收，在垃圾回收时，会暂停所有的用户线程，所以不适合服务器环境
 *      2、并行收集器，该收集器是为多个垃圾回收线程并行工作，在垃圾回收时，也会暂停所有用户线程，但是暂停时间非常的短暂，适用于科学计算/大数据处理等弱交互场景，jdk1.8默认的垃圾收集器
 *      3、CMS收集器，该收集器是用户线程和多个垃圾回收线程同时执行，在垃圾回收时，不需要暂停用户线程，适用于互联网公司和对响应时间有要求的公司
 *      4、G1收集器，该收集器是将堆内存分割成不同的区域，然后并发的进行垃圾回收，效率非常高
 *   查看jdk默认的收集器可以通过java命令：
 *      java -XX:+PrintCommandLineFlags -version
 *      结果：
 *          -XX:InitialHeapSize=132029760 -XX:MaxHeapSize=2112476160 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
        -XX:+UseParallelGC 该值为垃圾收集器
     jvm的默认垃圾收集器有多少种：
        由于jvm采用的分代收集算法，而jvm对堆内存分成了三分之一的年轻代和三分之二的老年代，所以按代收集可分为7种垃圾收集器，分别为：
            （1）年轻代有：
                1、Serial 串行垃圾收集器
                    对应JVM参数是：-XX:+UseSeriaIGC开启
                    使用：Serial（Young区用）+ Serial Old（od区用）的收集器组合
                    表示：新生代、老年代都会使用串行回收收集器，新生代使复制算法，老年代使用标记整理算法
                2、ParNew 并行垃圾收集器
                    对应JVM参数：-XX:+UseParNewGC启用
                    ParDew收集器，只影响新生代的收集，不影响老年代开启上述参数后，
                    会使用：ParNew （Young区用）+ Serial Old的收集器组合，
                    表示：新生代使用复制算法，老年代采用标记-整理算法
                3、ParallelScavenge 并行垃圾收集器
                    对应JVM参数：-XX:+UseParalleIGC或-XX:+UseParalleloldGc（可互相激活）
                    使用 Parallel Scavenge收集器开启该参数后：新生代使用复制算法，老年代使用标记-整理算法
            （2）养老代有：
                1、CMS 并发标记清除收集器
                    开启该收集器的JWM参数：-XX:+UseConcMarkSweepGC开启该参数后会自动将-XX+UseParNewGC打开开启该参数后，
                    使用 ParNew（ Young区用）+CMS（Od区用）+ Serial Old的收集器组合， Serial Old将作为CMS出错的后备收集器
                2、SerialOld（MSC） 老年代串行收集器
                    Serial Old。是 Serial垃圾收集器老年代版本，它同样是个单线程的收集器，使用标记-整理算法，这个收集器也主要是运行在cent默认的java虚拟机默认的年老代垃圾收集器。
                3、ParallelOld 老年代并行收集器
                    对应JVM参数：-XX:+UseParalleloldGC
                    使用 Parallel Old收集器，设置该参数后，新生代 Paralle+老年代 Parallel Old
            (3) G1垃圾收集器
                G1垃圾收集器既不属于年轻代也不属于老年代，它是一个横跨年轻代和老年代的一种垃圾回收器
                G1是什么？
                    G1是一种服务器端的垃圾收集器，应用在多处理器和大容量内存环境中，在实现髙吞吐量的同时，尽可能的满足垃圾收集暂停时间的要求。另外，它还具有以下特性像CMS收集器一样，能与应用程序线程并发执行整理空闲空间更快。
                G1收集器的特点：
                     1：G1能充分利用多CPU、多核环境硬件优势，尽量缩短STW
                     2：G1整体上采用标记-整理算法，局部是通过复制算法，不会产生内存碎片。
                     3：宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域（ Region），可以近似理解为一个围棋的棋盘。
                     4：G1收集器里面讲整个的内存区都混合在一起了，但其本身依然在小范围内要进行年轻代和老年代的区分，保留了新生代和老年代但它们不再是物理隔离的，而是王部分 Region的集合且不需要 Region，是连续的，也就是说依然会采用不同的GC方式来处理不同的区域5：G1虽然也是分代收集器，但整个内存分区不存在物理上的年轻代与老年代的区别，也不需要完全独立的 survivor（ to space）堆做复制准备。G1只有逻辑上的分代概念，或者说每个分区都可能随G1的运行在不同代之间前后切换
                G1收集器垃圾回收步骤有4步：
                    1、初始标记
                    2、并发标记
                    3、最终标记
                    4、垃圾清除
                对应JVM的参数：-XX:+UseGlGC
    JVM一般情况下配置哪些参数：
        -Xms1024m -Xmx1024m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
    如果对springBoot项目进行jvm调优性能配置：
        java -server [jvm调优参数] -jar [springboot项目jar包]
        比如配置一个test.jar jar包的springboot项目使用jvm堆内存初始化大小和最大大小为1024，并且使用G1垃圾回收器，配置命令如下:
            java -server -Xms1024m -Xmx1024m -XX:+UseG1GC -jar test.jar
    查看springboot项目jvm配置内容，可以使用以下2步获取：
        1、jps -l 获取jvm中正在运行的springboot项目线程编号
        2、jinfo -flags [线程编号]

 */
public class GCCase {

    public static void main(String[] args) {
        //strongReference();
        //softReference();
        //weakReference();
        //phantomReference();

        OOM();
    }

    public static void OOM(){
        String str = "abc";
        while (true){
            str+=str+new Random().nextInt(77777777)+new Random().nextInt(88888888);
        }
    }
    /**
     * 强引用
     * 即使JVM爆了，也不会清除
     */
    public static void strongReference(){
        //在java中默认这样就是强引用
        Object obj = new Object();

        Object obj2 = obj;

        obj = null;

        System.gc();

        System.out.println(obj2);

    }

    /**
     * 软引用
     * 如果jvm内存够，不会被清除，如果jvm内存不够将会被清除
     */
    public static void softReference(){
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        o1 = null;
        System.gc();

        System.out.println(o1);
        System.out.println(softReference.get());
    }

    /**
     * 弱引用
     *  只有gc进行垃圾回收，立马会被清除
     */
    public static void weakReference(){
        Object o1 = new Object();
        WeakReference<Object> weakReference = new WeakReference<>(o1);
        System.out.println(o1);
        System.out.println(weakReference.get());

        o1 = null;
        System.gc();

        System.out.println(o1);
        System.out.println(weakReference.get());
    }

    /**
     * 虚引用
     *  虚引用必须和引用队列配合使用，在队列执行完毕会，直接清除
     */
    public static void phantomReference(){
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference<>(o1,referenceQueue);

        System.out.println(o1);
        System.out.println(referenceQueue.poll());
        System.out.println(phantomReference.get());
    }

}
